package org.apache.guacamole.redis;

import redis.clients.jedis.Jedis;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 *
 * @author william
 * @date 2015-3-29
 */
public class RedisDaoImpl implements RedisDao {

    private final String host;
    private final int port;
    /**
     * 0-不启用，
     * 1-启用
     */
    private int used;

    public RedisDaoImpl(
            String host,
            int port,
            int used) {
        this.host = host;
        this.port = port;
        this.used = used;
    }


    private boolean isUsed() {
        return this.used != 0;
    }

    @Override
    public String set(String key, byte[] value) {
        if (!isUsed()) {
          return null;
        }

        String setStatus = "";
        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                setStatus = jedis.set(key.getBytes(UTF_8), value);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return setStatus;
    }

    @Override
    public String set(String key, byte[] value, long expiredTime) {
        if (!isUsed()) {
          return  null;
        }

        String setStatus = "";
        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                var keyBytes = key.getBytes(UTF_8);
                setStatus = jedis.set(keyBytes, value);
                jedis.expireAt(keyBytes, expiredTime);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return setStatus;
    }

    @Override
    public String setAndExpire(String key, byte[] value, final long seconds) {
        if (!isUsed()) {
          return  null;
        }

        String setStatus = "";
        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                jedis.setex(key.getBytes(UTF_8), seconds, value);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return setStatus;
    }

    @Override
    public String setAndExpireAt(String key, byte[] value, long unixTime) {
        if (!isUsed()) {
          return  null;
        }

        String setStatus = "";
        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                var keyBytes = key.getBytes(UTF_8);
                setStatus = jedis.set(keyBytes, value);
                jedis.expireAt(keyBytes, unixTime);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return setStatus;
    }

    @Override
    public int addToList(String key, String value) {
        int retCode=0;
        if (!isUsed()) {
          return  0;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                retCode = (jedis.rpush(key, value)).intValue();
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return retCode;
    }

    @Override
    public String getFromList(String key) {
        String  retVal=null;
        if (!isUsed()) {
            return  retVal;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                retVal = jedis.lpop(key);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return retVal;
    }


    @Override
    public int getListCount(final String key) {
        int retCode=0;
        if (!isUsed()) {
          return  0;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                retCode = jedis.llen(key).intValue();
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return retCode;
    }

    /**
     * 获取redis list中所有的内容,然后清空
     * @param key
     * @return
     */
    @Override
    public List<String> getAllAndEmptyList(final String key) {
        if (!isUsed()) {
          return new ArrayList<>();
        }


        List<String> results = null;

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis != null) {
                results = jedis.lrange(key, 0, -1);
                jedis.del(key);
            }
        } catch (Exception ex) {
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return results;
    }

    @Override
    public byte[] get(String key) {
        if (!isUsed()) {
          return null;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        byte[] objByte = new byte[0];
        try {
            if (jedis != null) {
              objByte = jedis.get(key.getBytes(UTF_8));
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

        return objByte;
    }

    @Override
    public String getStr(String key) {
        return getStr(key, UTF_8);
    }

    @Override
    public String getStr(String key, Charset charset) {
        byte[] bytes = get(key);

        return bytes == null ? null : new String(bytes, charset);
    }

    @Override
    public boolean exists(String key) {
        if (!isUsed()) {
          return false;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis == null) {
              return false;
            } else {
              return jedis.exists(key);
            }
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }
    }

    @Override
    public int remove(String key) {
        if (!isUsed()) {
          return 0;
        }

        Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
        try {
            if (jedis == null) {
              return 0;
            }

            return jedis.del(key).intValue();
        } finally {
            JedisUtil.getInstance().closeJedis(jedis, host, port);
        }

    }

    @Override
    public Set<String> smembers(String key) {
        Set<String> retVal = null;
        if (isUsed()) {

            Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
            try {
                if (jedis != null) {
                  retVal = jedis.smembers(key);
                }
            } finally {
                JedisUtil.getInstance().closeJedis(jedis, host, port);
            }
        }

        return retVal == null ? new HashSet<>() : retVal;
    }

    @Override
    public long scard(String key) {
        long retVal = 0;
        if (isUsed()) {

            Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
            try {
                if (jedis != null) {
                  retVal = jedis.scard(key);
                }
            } finally {
                JedisUtil.getInstance().closeJedis(jedis, host, port);
            }
        }

        return retVal;
    }

    @Override
    public long sadd(String key, List<String> elements) {
        long retVal = 0;
        if (isUsed()) {

            Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
            try {
                if (jedis != null) {
                    for(String val: elements) {
                        retVal += jedis.sadd(key, val);
                    }
                }
            } finally {
                JedisUtil.getInstance().closeJedis(jedis, host, port);
            }
        }

        return retVal;
    }

    @Override
    public long sadd(String key, String value) {
        long retVal = 0;
        if (isUsed()) {
            Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
            try {
                if (jedis != null) {
                    retVal += jedis.sadd(key, value);
                }
            } finally {
                JedisUtil.getInstance().closeJedis(jedis, host, port);
            }
        }

        return retVal;
    }

    @Override
    public Set<String> sdiff(String key1, String key2) {
        Set<String> retVal = null;
        if (isUsed()) {

            Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
            try {
                if (jedis != null) {
                    retVal = jedis.sdiff(key1, key2);
                }
            } finally {
                JedisUtil.getInstance().closeJedis(jedis, host, port);
            }
        }

        return retVal == null? new HashSet<>(): retVal;
    }

  @Override
  public int incrApiCallCount(String key, long expiredSeconds, int maxCalls) {
      int retCode = 0;
      if (!isUsed()) {
          return retCode;
      }

      Jedis jedis = JedisUtil.getInstance().getJedis(host, port);
      try {
          if (jedis != null) {
              //检查下该key的ttl的返回值是否正常,这里总是会出现该KEY不自动删除的情况,难道是REDIS的bug? 2016-4-9
//                In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist but has no associated expire.
//                Starting with Redis 2.8 the return value in case of error changed:
//                    The command returns -2 if the key does not exist.
//                            The command returns -1 if the key exists but has no associated expire.



              Long curCount = jedis.incr(key);
              if (curCount == 1) {
                  jedis.expire(key, expiredSeconds);
              }

              if (curCount > maxCalls) {
                  retCode = 0;
              } else {
                  retCode = curCount.intValue();
              }
          }
      } finally {
          JedisUtil.getInstance().closeJedis(jedis, host, port);
      }

      return retCode;
  }

  public String getHost() {
        return host;
    }


    public int getPort() {
        return port;
    }



    public int getUsed() {
        return used;
    }

    public void setUsed(int used) {
        this.used = used;
    }
}
